﻿// Copyright 2006 - Ricardo Stuven (rstuven@gmail.com)
//
// This file is part of MsSqlSpatial.
// MsSqlSpatial is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// MsSqlSpatial is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public License
// along with MsSqlSpatial; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using GeoAPI.Geometries;
using Microsoft.SqlServer.Server;
using MsSqlSpatial;
using UDF = UserDefinedFunctions;

public partial class TableValuedFunctions
{
    internal const string IsWithinDistanceQuery_FillRow = "FillRowIsWithinDistanceQuery";
    internal const string IsWithinDistanceQuery_TableDefinition = "oid bigint, distance float";

    /// <summary>
    /// 
    /// </summary>
    /// <param name="table_name"></param>
    /// <param name="column_name"></param>
    /// <param name="ewkb"></param>
    /// <param name="distance"></param>
    /// <returns></returns>
    /// <example>
    /// Usage:
    ///		SELECT t.* 
    ///		FROM ST.IsWithinDistanceQuery('GEOTABLE', 'GEOCOLUMN', ST.GeomFromText('POINT(1000 1000)',-1), 100) AS q
    ///		INNER JOIN GEOTABLE AS t ON q.oid = t.oid
    /// </example>
    [SpatialQuery]
    [SqlFunction(
        FillRowMethodName = IsWithinDistanceQuery_FillRow
        , TableDefinition = IsWithinDistanceQuery_TableDefinition
        , DataAccess = DataAccessKind.Read
        , SystemDataAccess = SystemDataAccessKind.Read
        )]
    public static IEnumerable IsWithinDistanceQuery(
        SqlString table_name,
        SqlString column_name,
        SqlBytes ewkb,
        SqlDouble distance)
    {
        return SpatialQuery.Execute(table_name, column_name, UserDefinedFunctions.Expand(ewkb, distance), ewkb,
                                    new SpatialQuery.Arguments(distance), IsWithinDistanceQueryHandler);
    }

    [SpatialQuery]
    [SqlFunction(
        FillRowMethodName = IsWithinDistanceQuery_FillRow
        , TableDefinition = IsWithinDistanceQuery_TableDefinition
        , DataAccess = DataAccessKind.Read
        , SystemDataAccess = SystemDataAccessKind.Read
        )]
    public static IEnumerable IsWithinDistanceQueryWhere(
        SqlString table_name,
        SqlString column_name,
        SqlBytes ewkb,
        SqlDouble distance,
        SqlString condition)
    {
        return SpatialQuery.Execute(table_name, column_name, UserDefinedFunctions.Expand(ewkb, distance), ewkb,
                                    condition, new SpatialQuery.Arguments(distance), IsWithinDistanceQueryHandler);
    }


    public static void FillRowIsWithinDistanceQuery(Object obj, out SqlInt64 oid, out SqlDouble distance)
    {
        oid = new SqlInt64((Int64) ((object[]) obj)[0]);
        distance = new SqlDouble((double) ((object[]) obj)[1]);
    }

    private static IEnumerable IsWithinDistanceQueryHandler(SqlDataReader reader, IGeometry geometry,
                                                            SpatialQuery.Arguments args)
    {
        double distance = args.double1.Value;
        var rows = new List<object[]>();
        while (reader.Read())
        {
            IGeometry _geometry = ConvertGeometry.ToGeometry(reader.GetSqlBytes(1));

            //Geometry::IsWithinDistance checks EnvelopsInternal. This is not needed here.
            //if (geometry.IsWithinDistance(_pair.Geometry, distance))
            double currentDistance = geometry.Distance(_geometry);
            if (currentDistance <= distance)
            {
                rows.Add(new object[] {(long) reader.GetValue(0), currentDistance});
            }
        }
        return rows;
    }
} ;